package com.miaoshare.core.utils.webViewUtil;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;

import androidx.annotation.Nullable;

import com.luck.picture.lib.utils.ToastUtils;
import com.miaoshare.core.R;
import com.miaoshare.core.base.BaseActivity;
import com.miaoshare.core.base.BasePresenter;
import com.miaoshare.core.utils.AppUtils;
import com.miaoshare.core.utils.LogUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 * Created by zxs
 * on 2022/2/11
 * * WebView是一个基于webkit引擎、展现web页面的控件。
 * * 4.4后直接使用了Chrome。
 * *
 * * 此Activity仅仅介绍 WebView 的一些用法
 */
public class BaseWebActivity extends BaseActivity {
    /**
     * 微软在线预览office文档
     */
    private final String MicrosoftOnlinePreviewUrl = "https://view.officeapps.live.com/op/view.aspx?src=";
    LinearLayout linearLayout;
    private WebView webView;
    private ProgressDialog mProgressDialog;

    // 下载失败
    public static final int DOWNLOAD_ERROR = 2;
    // 下载成功
    public static final int DOWNLOAD_SUCCESS = 1;
    private File file;
    /**
     * 加载地址或文件内容
     */
    private String mUrl;
    /**
     * 加载类型
     */
    private int mUrlType;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_web;
    }

    @Override
    protected void initView() {
        //生成一个WebView组件（有两种方式）
        //方式1：直接在在Activity中生成
        //WebView webView = new WebView(this)

        //方法2：在Activity的layout文件里添加webview控件：
        //WebView webview = (WebView) findViewById(R.id.webView1);
        linearLayout = findViewById(R.id.linear_layout);
        webView = findViewById(R.id.webview);
//        initWebView();
        //可自行定义接受key
        mUrl = getIntent().getStringExtra("URL_CONTENT");
        LogUtil.d(mUrl);
        mUrlType = getIntent().getIntExtra("URL_TYPE", 0);
    }

    @Override
    protected void loadData() {

    }

    @Nullable
    @Override
    protected BasePresenter initPresenter() {
        return null;
    }

    /**
     * 避免WebView内存泄露？
     * 不在xml中定义 Webview ，而是在需要的时候在Activity中创建，
     * 并且Context使用 getApplicationgContext()
     */
//    private void initWebView() {
//        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
//        mWebView = new WebView(getApplicationContext());
//        mWebView.setLayoutParams(params);
//        linearLayout.addView(mWebView);
//    }
    @Override
    protected void onResume() {
        super.onResume();
//激活WebView为活跃状态，能正常执行网页的响应
        webView.onResume();

        setWebSetting();
        setWebClient();
        setWebChromeClient();
        setWebJs();
        //恢复pauseTimers状态
        webView.resumeTimers();
    }

    /**
     * 方法一、通过addJavascriptInterface()将Java对象映射到JS对象
     * /参数1：Javascript对象名
     * 参数2：Java对象名
     */
    private void setWebJs() {
        //仅仅将Android对象和JS对象映射--存在严重的漏洞问题  test 自行约定
        webView.addJavascriptInterface(new JsToAndroid(this, webView), "test");//JsToAndroid类对象映射到js的test对象
    }

    /**
     * 1. WebSettings类
     * 作用：对WebView进行配置和管理
     */
    @SuppressLint("SetJavaScriptEnabled")
    private void setWebSetting() {
        //1.声明WebSettings子类
        WebSettings webSettings = webView.getSettings();
        //如果访问的页面中要与Javascript交互，则webview必须设置支持Javascript
        webSettings.setJavaScriptEnabled(true);//（默认是不允许）

        webSettings.setSavePassword(false);     //关闭密码保存提醒,默认TRUE
        webSettings.setSaveFormData(false);

        //2.支持插件
//        webSettings.setPluginsEnabled(true);

        //3.设置自适应屏幕，两者合用
        webSettings.setUseWideViewPort(true);//将图片调整到适合webview的大小
        webSettings.setLoadWithOverviewMode(true);// 缩放至屏幕的大小

        //4.缩放操作
        webSettings.setSupportZoom(true);//支持缩放，默认为true。是下面那个的前提。
        webSettings.setBuiltInZoomControls(true);//设置内置的缩放控件。若为false，则该WebView不可缩放
        webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件

        //其他细节操作
        //5. 缓存模式如下：//        结合使用（离线加载）
//        判断有无WiFi状态
        //LOAD_CACHE_ONLY: 不使用网络，只读取本地缓存数据
        //LOAD_DEFAULT: （默认）根据cache-control决定是否从网络上取数据。
        //LOAD_NO_CACHE: 不使用缓存，只从网络获取数据.
        //LOAD_CACHE_ELSE_NETWORK，只要本地有，无论是否过期，或者no-cache，都使用缓存中的数据。
        webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存(在可用的时候使用缓存资源，即使它们已经过期了。否则将从网络加载资源。)

        webSettings.setAllowFileAccess(true);       //设置可以访问文件,Chrome 默认禁止加载【安全问题】--禁用的话不能加载本地html
//        webSettings.setAllowFileAccessFromFileURLs(true); //是否允许通过 file url 加载的 Js代码读取其他的本地文件,在Android 4.1后默认禁止【安全问题】
//        webSettings.setAllowUniversalAccessFromFileURLs(true);    //设置是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源),在Android 4.1后默认禁止【安全问题】

        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);//支持通过JS打开新窗口
        webSettings.setLoadsImagesAutomatically(true);//支持自动加载图片
        webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式

        /**
         * 缓存
         */
        //1.Application Cache 缓存机制
        webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
//        每个 Application 只调用一次 WebSettings.setAppCachePath()，WebSettings.setAppCacheMaxSize()
        String cacheDirPath = getFilesDir().getAbsolutePath() + this.getClass().getName() + "cache/";
        webSettings.setAppCachePath(cacheDirPath); //设置  Application Caches 缓存目录
        webSettings.setAppCacheMaxSize(20 * 1024 * 1024);   // 设置缓存大小(未来的配额将自动管理。)

        //2. Dom Storage 缓存机制
        webSettings.setDomStorageEnabled(true);// 开启 DOM storage API 功能

        //3. Web SQL Database 缓存机制
        //根据官方说明，Web SQL Database存储机制不再推荐使用（不再维护）取而代之的是 IndexedDB缓存机制
        String cacheDirPath2 = this.getFilesDir().getAbsolutePath() + "cache/";
        webSettings.setDatabasePath(cacheDirPath2);     //方法应该只被调用一次:重复调用被忽略。数据库路径由实现管理，调用此方法将无效果。
        webSettings.setDatabaseEnabled(true); //开启 database storage API 功能,// 开启 数据库存储机制

        //4. IndexedDB 缓存机制
        // 只需设置支持JS就自动打开IndexedDB存储机制,Android 在4.4开始加入对 IndexedDB 的支持，只需打开允许 JS 执行的开关就好了
        //webSettings.setJavaScriptEnabled(true);

    }


    /**
     * 2. WebViewClient类
     * 处理各种通知 & 请求事件
     */
    private void setWebClient() {
//        常见方法1：shouldOverrideUrlLoading()
//        作用：打开网页时不调用系统浏览器， 而是在本WebView中显示；
// 在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。
        loadContent(mUrl, mUrlType);
        webView.setWebViewClient(new WebViewClient() {
            //1. 复写shouldOverrideUrlLoading()方法，使得打开网页时不调用系统浏览器， 而是在本WebView中显示
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
//                webView.loadUrl(url);
                return true;
            }

            //2. 作用：开始载入页面调用的，我们可以设定一个loading的页面，告诉用户程序在等待网络响应。
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                //设定加载开始的操作
            }

            //3.作用：在页面加载结束时调用。我们可以关闭loading 条，切换程序动作。
            @Override
            public void onPageFinished(WebView view, String url) {
                //设定加载结束的操作
            }

            //4. 作用：在加载页面资源时会调用，每一个资源（比如图片）的加载都会调用一次。
            @Override
            public void onLoadResource(WebView view, String url) {
                //设定加载资源的操作
            }


            //5. 遇到了诸如404这类的错误的时候，若也显示浏览器里面的那种错误提示页面就显得很丑陋了，那么这个时候我们的app就需要加载一个本地的错误提示页面，即webview如何加载一个本地的页面
            //步骤1：写一个html文件（error_handle.html），用于出错时展示给用户看的提示页面
            //步骤2：将该html文件放置到代码根目录的assets文件夹下
            //步骤3：复写WebViewClient的onRecievedError方法
            //该方法传回了错误码，根据错误类型可以进行不同的错误分类处理
            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                switch (errorCode) {
                    case 404:
                        webView.loadUrl("file:///android_assets/error_handle.html");
                        break;
                }
            }


            //6. 处理https请求
            //webView默认是不处理https请求的，页面显示空白，需要进行如下设置：
            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                handler.proceed();//表示等待证书响应
//                handler.cancel();//表示挂起连接，为默认方式
//                handler.handleMessage(null);//可做其他处理
            }

            //7. 当向服务器访问这些静态资源时进行拦截
            // API 21 以下用shouldInterceptRequest(WebView view, String url)
            // API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                // 步骤1:判断拦截资源的条件，即判断url里的图片资源的文件名
                if (url.contains("logo.gif")) {
                    // 假设网页里该图片资源的地址为：http://abc.com/imgage/logo.gif
                    // 图片的资源文件名为:logo.gif
                    InputStream is = null;
                    // 步骤2:创建一个输入流
                    try {
                        is = getApplicationContext().getAssets().open("images/abc.png");

                        // 步骤3:获得需要替换的资源(存放在assets文件夹里)
                        // a. 先在app/src/main下创建一个assets文件夹
                        // b. 在assets文件夹里再创建一个images文件夹
                        // c. 在images文件夹放上需要替换的资源（此处替换的是abc.png图片）
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    // 步骤4:替换资源
                    // 参数1：http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2：编码类型
                    // 参数3：存放着替换资源的输入流（上面创建的那个）
                    WebResourceResponse response = new WebResourceResponse("image/png", "utf-8", is);

                    return response;
                }
                return super.shouldInterceptRequest(view, url);
            }


            //7.1
            // API 21 以上用shouldInterceptRequest(WebView view, WebResourceRequest request)
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
                // 步骤1:判断拦截资源的条件，即判断url里的图片资源的文件名
                if (request.getUrl().toString().contains("logo.gif")) {
                    // 假设网页里该图片资源的地址为：http://abc.com/imgage/logo.gif
                    // 图片的资源文件名为:logo.gif
                    InputStream is = null;
                    // 步骤2:创建一个输入流

                    try {
                        is = getApplicationContext().getAssets().open("images/abc.png");
                        // 步骤3:获得需要替换的资源(存放在assets文件夹里)
                        // a. 先在app/src/main下创建一个assets文件夹
                        // b. 在assets文件夹里再创建一个images文件夹
                        // c. 在images文件夹放上需要替换的资源（此处替换的是abc.png图片
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    // 步骤4:替换资源
                    // 参数1：http请求里该图片的Content-Type,此处图片为image/png
                    // 参数2：编码类型
                    // 参数3：存放着替换资源的输入流（上面创建的那个）
                    WebResourceResponse response = new WebResourceResponse("image/png", "utf-8", is);

                    return response;
                }
                return super.shouldInterceptRequest(view, request);
            }

        });
    }


    /**
     * 3. WebChromeClient类
     * 辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。
     */
    private void setWebChromeClient() {
        webView.setWebChromeClient(new WebChromeClient() {

            //1.获得网页的加载进度并显示
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                if (newProgress < 100) {
                    String progress = newProgress + "%";
//                    progress.setText(progress);
                }
            }

            //2. 获取Web页中的标题 每个网页的页面都有一个标题，比如www.baidu.com这个页面的标题即“百度一下，你就知道”
//           可以在该方法监听网页加载异常
            @Override
            public void onReceivedTitle(WebView view, String title) {
//                titleview.setText(title);
                super.onReceivedTitle(view, title);
                Log.e("title", "title---" + title);
                //171016 处理404错误 android 6.0 以下通过title获取,网页错误销毁页面index
                if (title.contains("404") || title.contains("about:blank") || title.contains("500") || title.contains("Error") || title.contains("网页无法打开")) {
//                    webView.loadUrl("about:blank");
                    ToastUtils.showToast(BaseWebActivity.this, title);
                } else {
                    //进行加载成功的操作
                }
            }
        });
    }


    /**
     * 加载不同内容处理
     */
    private void loadContent(String content, int contentType) {
        switch (contentType) {
            case 0://加载网页
                webView.loadUrl(content);
                break;
            case 2://加载 apk内置html,content文件名
                webView.loadUrl("file:///android_asset/" + content);
                break;
            case 3://加载手机本地的html页面
                webView.loadUrl("content://com.android.htmlfileprovider/sdcard/" + content);
                break;
            case 4://是office文档，使用 微软在线预览地址+文件地址 预览，Google在线预览需会存在问题
                if (AppUtils.isOffice(content)) {
                    webView.loadUrl(MicrosoftOnlinePreviewUrl + content);
                }
                break;
            case 5://pdf
                final String Strname = content;
                mProgressDialog = new ProgressDialog(this);
                mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
                mProgressDialog.setCancelable(false);
                mProgressDialog.show();
                //截取最后14位 作为文件名
                String s = Strname.substring(Strname.length() - 14);
                //文件存储
                file = new File(Environment.getExternalStorageDirectory(), AppUtils.getFileName(s));
                new Thread() {
                    public void run() {
                        File haha = new File(file.getAbsolutePath());
                        //判断是否有此文件
                        if (haha.exists()) {
                            //有缓存文件,拿到路径 直接打开
                            Message msg = Message.obtain();
                            msg.obj = haha;
                            msg.what = DOWNLOAD_SUCCESS;
                            handler.sendMessage(msg);
                            mProgressDialog.dismiss();
                            return;
                        }
//              本地没有此文件 则从网上下载打开
                        File downloadfile = AppUtils.downLoad(Strname, file.getAbsolutePath(), mProgressDialog);
//              Log.i("Log",file1.getAbsolutePath());
                        Message msg = Message.obtain();
                        if (downloadfile != null) {
                            // 下载成功,安装....
                            msg.obj = downloadfile;
                            msg.what = DOWNLOAD_SUCCESS;
                        } else {
                            // 提示用户下载失败.
                            msg.what = DOWNLOAD_ERROR;
                        }
                        handler.sendMessage(msg);
                        mProgressDialog.dismiss();
                    }
                }.start();
                break;
        }

    }


    /**
     * 下载完成后  直接打开文件
     */
    @SuppressLint("HandlerLeak")
    Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case DOWNLOAD_SUCCESS:
                    File file = (File) msg.obj;
                    Intent intent = new Intent("android.intent.action.VIEW");
                    intent.addCategory("android.intent.category.DEFAULT");
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.setDataAndType(Uri.fromFile(file), "application/pdf");
//              startActivity(intent);
                    startActivity(Intent.createChooser(intent, "标题"));
                    /**
                     * 弹出选择框   把本activity销毁
                     */
                    finish();
                    break;
                case DOWNLOAD_ERROR:
//                    Util.showToast(act, "文件加载失败");
                    break;
            }
        }
    };

    /********************-----------------***************/
    @Override
    protected void onPause() {
        super.onPause();
        //当页面被失去焦点被切换到后台不可见状态，需要执行onPause
        //通过onPause动作通知内核暂停所有的动作，比如DOM的解析、plugin的执行、JavaScript执行。
        webView.onPause();

        //当应用程序(存在webview)被切换到后台时，这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
        //它会暂停所有webview的layout，parsing，javascripttimer。降低CPU功耗。
        webView.pauseTimers();
    }


    /*****----------------**************/
    //此方法兼容Android 1.0到Android 2.1 也是常规方法
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        //是否可以后退
//        webView.canGoBack();
//        //后退网页
//        webView.goBack();
//
//        //是否可以前进
//        webView.canGoForward();
//        //前进网页
//        webView.goForward();
//
//        //以当前的index为起始点前进或者后退到历史记录中指定的steps
////如果steps为负数则为后退，正数则为前进
//        webView.goBackOrForward(1);


        // 是否触发按键为back键
        if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
            webView.goBack();
//            onBackPressed();
            return true;
        } else {
            return super.onKeyDown(keyCode, event);
        }
    }

    // 退出时间
    private long currentBackPressedTime = 0;
    // 退出间隔
    private static final int BACK_PRESSED_INTERVAL = 2000;

    //该方法仅适用于2.0或更新版的sdk
//    @Override
//    public void onBackPressed() {
//        // 判断时间间隔
//        if (System.currentTimeMillis() - currentBackPressedTime > BACK_PRESSED_INTERVAL) {
//            currentBackPressedTime = System.currentTimeMillis();
//            Toast.makeText(this, "再按一次返回键退出程序", Toast.LENGTH_SHORT).show();
//        } else {
//            //点击的时间差小于2000，调用父类onBackPressed方法执行退出。
//            super.onBackPressed();
//        }
//    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //清除网页访问留下的缓存
        //由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
        webView.clearCache(true);

        //清除当前webview访问的历史记录
        //只会webview访问历史记录里的所有记录除了当前访问记录
        webView.clearHistory();


        //这个api仅仅清除自动完成填充的表单数据，并不会清除WebView存储到本地的数据
        webView.clearFormData();

        //销毁Webview
        //在关闭了Activity时，如果Webview的音乐或视频，还在播放。就必须销毁Webview
        //但是注意：webview调用destory时,webview仍绑定在Activity上
        //这是由于自定义webview构建时传入了该Activity的context对象
        //因此需要先从父容器中移除webview,然后再销毁webview:
        linearLayout.removeView(webView);
        webView.destroy();

        /**-或者-
         * 在 Activity 销毁（ WebView ）的时候，先让 WebView 加载null内容，
         * 然后移除 WebView，再销毁 WebView，最后置空。
         * **/
//        if (mWebView != null) {
//            mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
//            mWebView.clearHistory();
//
//            ((ViewGroup) mWebView.getParent()).removeView(mWebView);
//            mWebView.destroy();
//            mWebView = null;
//        }
    }
}
